/**
 * CacheAspect.java 
 * Copyright © 2015-3015, 杭州泊享网络科技有限公司
 * 
 * @author Liu Tao
 * @create 2014年10月29日
 */
package com.pshare.framework.aop;

import java.io.Serializable;
import java.lang.reflect.Method;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import com.pshare.cache.service.RedisService;
import com.pshare.constant.SeaxConstants;
import com.pshare.framework.annotation.CacheEvictx;
import com.pshare.framework.annotation.CachePutx;
import com.pshare.framework.annotation.Cacheablex;

/**
 * 缓存切面
 */
@Component
@Aspect
public class CacheAspect {

	@Autowired
	private RedisService redisService;

	/**
	 * 读取缓存，如果缓存为空，则将数据保存到缓存
	 * value: 缓存的key值;
	 * 	 如果带@，表示储存到set中，@后的数字表示小时数；
	 *   如果带#，#后的值表示过期时间，不带则使用默认过期时间; 
	 *   如果不带@，且带#，表示以对象方式保存；
	 *   如果既不带#又不带@，表示以hash方式保存。
	 * key: 缓存的子key值
	 * condition: 条件表达式
	 */
	@Around("@annotation(com.pshare.framework.annotation.Cacheablex)")
	public Object cache(ProceedingJoinPoint pjp) {
		Object result = null;

		Method method = getMethod(pjp);
		Cacheablex cacheable = method.getAnnotation(Cacheablex.class);

		String key = StringUtils.isBlank(cacheable.key()) ? null : parseKey(cacheable.key(), method, pjp.getArgs());

		// 获取方法的返回类型,让缓存可以返回正确的类型
		Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType();

		// 使用redis 的hash进行存取，易于管理
		String[] values = cacheable.value();
		for (String value : values) {
			Long expire = null;
			String storeat = null;
			String[] va = value.split(SeaxConstants.AT);
			String va1 = va[0];
			if (va.length > 1) {
				storeat = va[1];
			}
			String[] vp = va1.split(SeaxConstants.SHARP);
			String vx = vp[0];
			if (vp.length > 1) {
				String expireTime = vp[1];
				try {
					expire = Long.parseLong(expireTime);
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}

			if (storeat != null) {
				result = redisService.getTimeSet(key, Integer.parseInt(storeat));
			} else if (key != null && expire == null) {
				result = redisService.getHashValue(vx, key);
			} else {
				String realKey = key == null ? vx : vx + "_" + key;
				result = redisService.readCache(realKey);
			}

			if (result == null) {
				try {
					result = pjp.proceed();
					
					if (storeat != null) {
						redisService.addToTimeSet(key, value);
					} else if (key != null && value.indexOf("#") == -1) {
						redisService.addToHash(vx, key, (Serializable) result);
					} else {
						String realKey = key == null ? vx : vx + "_" + key;
						redisService.updateCache(realKey, (Serializable) result, expire);
					}
				} catch (Throwable e) {
					e.printStackTrace();
				}
			}
		}

		return result;
	}

	/** * 定义清除缓存逻辑 */
	@Around(value = "@annotation(com.pshare.framework.annotation.CacheEvictx)")
	public void evict(ProceedingJoinPoint pjp) {
		Method method = getMethod(pjp);
		CacheEvictx cacheevict = method.getAnnotation(CacheEvictx.class);
		String key = StringUtils.isBlank(cacheevict.key()) ? null : parseKey(cacheevict.key(), method, pjp.getArgs());

		// 使用redis 的hash进行存取，易于管理
		String[] values = cacheevict.value();
		for (String value : values) {
			Long expire = null;
			String storeat = null;
			String[] va = value.split(SeaxConstants.AT);
			String va1 = va[0];
			if (va.length > 1) {
				storeat = va[1];
			}
			String[] vp = va1.split(SeaxConstants.SHARP);
			String vx = vp[0];
			if (vp.length > 1) {
				String expireTime = vp[1];
				try {
					expire = Long.parseLong(expireTime);
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}

			try {
				pjp.proceed();
				
				if (storeat != null) {
					redisService.removeExpiredSet(key, Integer.parseInt(storeat));
				} else if (key != null && value.indexOf("#") == -1) {
					redisService.removeFromHashMap(vx, key);
				} else {
					String realKey = key == null ? vx : vx + "_" + key;
					redisService.deleteCache(realKey);
					
				}
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
	}

	/** * 定义添加缓存逻辑 */
	@Around(value = "@annotation(com.pshare.framework.annotation.CachePutx)")
	public void put(ProceedingJoinPoint pjp) {
		Object result = null;

		Method method = getMethod(pjp);
		CachePutx cacheput = method.getAnnotation(CachePutx.class);

		String key = StringUtils.isBlank(cacheput.key()) ? null : parseKey(cacheput.key(), method, pjp.getArgs());

		// 使用redis 的hash进行存取，易于管理
		String[] values = cacheput.value();
		for (String value : values) {
			Long expire = null;
			String storeat = null;
			String[] va = value.split(SeaxConstants.AT);
			String va1 = va[0];
			if (va.length > 1) {
				storeat = va[1];
			}
			String[] vp = va1.split(SeaxConstants.SHARP);
			String vx = vp[0];
			if (vp.length > 1) {
				String expireTime = vp[1];
				try {
					expire = Long.parseLong(expireTime);
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}

			try {
				result = pjp.proceed();
				if (result == null) {
					return;
				}
				
				if (storeat != null) {
					redisService.addToTimeSet(key, value);
				} else if (key != null && value.indexOf("#") == -1) {
					redisService.addToHash(vx, key, (Serializable) result);
				} else {
					String realKey = key == null ? vx : vx + "_" + key;
					redisService.updateCache(realKey, (Serializable) result, expire);
				}
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 获取被拦截方法对象
	 * 
	 * MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象 而缓存的注解在实现类的方法上
	 * 所以应该使用反射获取当前对象的方法对象
	 */
	public Method getMethod(ProceedingJoinPoint pjp) {
		// 获取参数的类型
//		Object[] args = pjp.getArgs();
//		Class[] argTypes = new Class[pjp.getArgs().length];
//		for (int i = 0; i < args.length; i++) {
//			if (args[i] != null)
//				argTypes[i] = args[i].getClass();
//		}
//		Method method = null;
//		try {
//			method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argTypes);
//		} catch (NoSuchMethodException e) {
//			e.printStackTrace();
//		} catch (SecurityException e) {
//			e.printStackTrace();
//		}
//		return method;
//
//		
		 MethodSignature joinPointObject = (MethodSignature) pjp.getSignature();  
	     Method method = joinPointObject.getMethod();
	     return method;
	}

	/**
	 * 获取缓存的key key 定义在注解上，支持SPEL表达式
	 * 
	 * @param pjp
	 * @return
	 */
	private String parseKey(String key, Method method, Object[] args) {

		// 获取被拦截方法参数名列表(使用Spring支持类库)
		LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
		String[] paraNameArr = u.getParameterNames(method);

		// 使用SPEL进行key的解析
		ExpressionParser parser = new SpelExpressionParser();
		// SPEL上下文
		StandardEvaluationContext context = new StandardEvaluationContext();
		// 把方法参数放入SPEL上下文中
		for (int i = 0; i < paraNameArr.length; i++) {
			context.setVariable(paraNameArr[i], args[i]);
		}
		return parser.parseExpression(key).getValue(context, String.class);
	}

}
